﻿
//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#include "GNodeUtility.h"
#include "IElement.h"
#include "IDocument.h"
#include "ElementId.h"
#include "IGraphicsNode.h"
#include "IGraphicsText.h"
#include "IGraphicsPoint.h"
#include "IGraphicsPlane.h"
#include "IGraphicsCurve3d.h"
#include "IGraphicsPicture.h"
#include "IGraphicsBRepBody.h"
#include "IGraphicsNodeList.h"
#include "IGraphicsBRepFace.h"
#include "IGraphicsBRepBody.h"
#include "IGraphicsMeshBody.h"
#include "IGraphicsBRepEdge.h"
#include "IGraphicsRichText.h"
#include "IGraphicsNodeList.h"
#include "IGraphicsGisLayer.h"
#include "IGraphicsZoomFree.h"
#include "IGraphicsGroupBase.h"
#include "IElementModelShape.h"
#include "IGraphicsNodeGroup.h"
#include "IGraphicsPolyCurve.h"
#include "IGraphicsPolyCurve3d.h"
#include "IGraphicsElementShape.h"
#include "IGraphicsNodeReference.h"
#include "IGraphicsNodePathCollector.h"
#include "IGraphicsElementShapeReference.h"
#include "IGraphicsElementShapeReference.h"
#include "ILinkElementModelShapeReference.h"
#include "IGraphicsOtherDocumentElementShapeReference.h"

#include <iterator>

#include "GNodeWrapper.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h" 


using namespace gcmp;
using namespace Sample;

namespace Sample
{
    // 实现IGraphicsNodePathTester用户获得图形节点的IGraphicsNodePath
    // IGraphicsNodePathTester还可以用于自定义导出
    class TypeAndIdGNodePathTester : public gcmp::IGraphicsNodePathTester
    {
    public:
        TypeAndIdGNodePathTester(GraphicsNodeType gNodeType, GraphicsNodeId gNodeId)
            : m_gNodeType(gNodeType), m_gNodeId(gNodeId) {}
        virtual ~TypeAndIdGNodePathTester() {}

    public:
        virtual bool IsDesiredGraphicsNodePath(const gcmp::IGraphicsNodePath& nodePath) const override
        {
            const GraphicsNodeAndTransform gNodeAndTrf = nodePath.GetLastGraphicsNodeAndTransform();
            if (gNodeAndTrf.m_wpGraphicsNode->GetType() == m_gNodeType && gNodeAndTrf.m_wpGraphicsNode->GetId() == m_gNodeId)
            {
                return true;
            }
            return false;
        }
    private:
        GraphicsNodeType m_gNodeType;
        GraphicsNodeId m_gNodeId;
    };
}

std::vector<GraphicsNodeId> GNodeUtility::CollectGNodeIds(const std::vector<const IGraphicsNode*>& pNodePath)
{
    std::vector<gcmp::GraphicsNodeId> gNodeIdPath;
    bool m_bAddIdToPath = false;

    for (auto& pGraphicsNode : pNodePath)
    {
        GraphicsNodeId nodeId = pGraphicsNode->GetId();
        std::wstring nodeIdStr = StringUtil::ToWString(nodeId.AsInt()) + L";";
        OutputDebugStringW(nodeIdStr.c_str());
        switch (pGraphicsNode->GetType())
        {
        case GraphicsNodeType::ElementShape:
        {
            if (m_bAddIdToPath)
            {
                gNodeIdPath.emplace_back(nodeId);
            }
            m_bAddIdToPath = true;
            break;
        }
        case GraphicsNodeType::ElementShapeReference:
        {
            m_bAddIdToPath = false;
            gNodeIdPath.emplace_back(nodeId);
            break;
        }
        case GraphicsNodeType::LinkedReference:
        {
            m_bAddIdToPath = false;
            if (nodeId.IsValid())
            {
                gNodeIdPath.emplace_back(nodeId);
            }
            break;
        }
        case GraphicsNodeType::DocumentReference:
        {
            m_bAddIdToPath = true;
            gNodeIdPath.emplace_back(nodeId);
            break;
        }
        case GraphicsNodeType::Group:
        case GraphicsNodeType::Body:
        case GraphicsNodeType::PolyCurve:
        {
            if (nodeId.IsValid())
            {
                gNodeIdPath.emplace_back(nodeId);
            }
            break;
        }
        case GraphicsNodeType::Point3d:
        case GraphicsNodeType::Curve3d:
        case GraphicsNodeType::Line3d:
        case GraphicsNodeType::Arc3d:
        case GraphicsNodeType::Ellipse3d:
        case GraphicsNodeType::NurbsCurve3d:
        case GraphicsNodeType::Face:
        case GraphicsNodeType::Edge:
        case GraphicsNodeType::Text:
        case GraphicsNodeType::Plane3d:
        case GraphicsNodeType::RichTex:
        case GraphicsNodeType::PolyCurve3d:
        {
            gNodeIdPath.emplace_back(nodeId);
            break;
        }
        case GraphicsNodeType::Picture:
        case GraphicsNodeType::Zoomfree:
        case GraphicsNodeType::MeshBody:
        case GraphicsNodeType::GISTile:
        {
            break;
        }
        default:
        {
            break;
        }
        }
    }
    OutputDebugStringW(L"\n");
    return gNodeIdPath;
}

void GNodeUtility::VisitGNode(IGraphicsNode* pGNode, IGraphicsNode_Wrapper* pGNodeWrapper)
{
    GraphicsNodeType type = pGNode->GetType();
    switch (type)
    {
    case GraphicsNodeType::Body:
        pGNodeWrapper->VisitGBody(quick_cast<IGraphicsBRepBody>(pGNode));
        break;
    case GraphicsNodeType::MeshBody:
        pGNodeWrapper->VisitGMeshBody(quick_cast<IGraphicsMeshBody>(pGNode));
        break;
    case GraphicsNodeType::Face:
        pGNodeWrapper->VisitGFace(quick_cast<IGraphicsBRepFace>(pGNode));
        break;
    case GraphicsNodeType::Edge:
        pGNodeWrapper->VisitGEdge(quick_cast<IGraphicsBRepEdge>(pGNode));
        break;
    case GraphicsNodeType::ElementShape:
        pGNodeWrapper->VisitGRep(quick_cast<IGraphicsElementShape>(pGNode));
        break;
    case GraphicsNodeType::Plane3d:
        pGNodeWrapper->VisitGPlane(quick_cast<IGraphicsPlane>(pGNode));
        break;
    case GraphicsNodeType::Group:
        pGNodeWrapper->VisitGGroup(quick_cast<IGraphicsNodeGroup>(pGNode));
        break;
    case GraphicsNodeType::Curve3d:
    case GraphicsNodeType::Line3d: // To be removed
    case GraphicsNodeType::Arc3d: // To be removed
    case GraphicsNodeType::Ellipse3d: // To be removed
    case GraphicsNodeType::NurbsCurve3d: // To be removed
        pGNodeWrapper->VisitGCurve(quick_cast<IGraphicsCurve3d>(pGNode));
        break;
    case GraphicsNodeType::Point3d:
        pGNodeWrapper->VisitGPoint(quick_cast<IGraphicsPoint>(pGNode));
        break;
    case GraphicsNodeType::Pointset:
        // 目前没有公开API
        break;
    case GraphicsNodeType::Text:
        pGNodeWrapper->VisitGText(quick_cast<IGraphicsText>(pGNode));
        break;
    case GraphicsNodeType::Picture:
        pGNodeWrapper->VisitGPicture(quick_cast<IGraphicsPicture>(pGNode));
        break;
    case GraphicsNodeType::ElementShapeReference:
        pGNodeWrapper->VisitGInstance(quick_cast<IGraphicsElementShapeReference>(pGNode));
        break;
    case GraphicsNodeType::DocumentReference:
        pGNodeWrapper->VisitGDocReference(quick_cast<IGraphicsOtherDocumentElementShapeReference>(pGNode));
        break;
    case GraphicsNodeType::LinkedReference:
        pGNodeWrapper->VisitGLinkReference(quick_cast<ILinkElementModelShapeReference>(pGNode));
        break;
    case GraphicsNodeType::RichTex:
        pGNodeWrapper->VisitRichText(quick_cast<IGraphicsRichText>(pGNode));
        break;
    case GraphicsNodeType::PolyCurve:
        pGNodeWrapper->VisitGPolyCurve(quick_cast<IGraphicsPolyCurve>(pGNode));
        break;
    case GraphicsNodeType::GISTile:
        pGNodeWrapper->VisitGISTile(quick_cast<IGraphicsGisLayer>(pGNode));
        break;
    case GraphicsNodeType::PolyCurve3d:
        pGNodeWrapper->VisitGPolyCurve3d(quick_cast<IGraphicsPolyCurve3d>(pGNode));
        break;
    case GraphicsNodeType::Zoomfree:
        pGNodeWrapper->VisitGZoomfree(quick_cast<IGraphicsZoomFree>(pGNode));
        break;
    default:
        DBG_WARN(L"不支持的GNode类型",L"GDMP",L"2024-03-30");
    }
}

OwnerPtr<IGraphicsNode_Wrapper> GNodeUtility::CreateGNodeWrapperTree(IDocument* pDoc, ElementId id, IGraphicsNode* pGNode, 
    IGraphicsNode* rootGNode, bool isReadOnly, IGraphicsNode_Wrapper* pParent)
{
    OwnerPtr<IGraphicsNode_Wrapper> opGNodeWrapper = NEW_AS_OWNER_PTR(IGraphicsNode_Wrapper, id, pGNode, isReadOnly, pParent);

    // 如果每次都从rootGNode获得IGraphicsNodePath，在处理大型GRep例如导出CAD时有性能问题。
    // 因此下面IGraphicsNodePathCollector只计算当前节点和父母节点之间的IGraphicsNodePath，然后累计

    // IGraphicsNodePath是一个图形节点在图形节点树中的从根节点经过的所有节点列表
    IGraphicsNode* pRootGNodeForCollector = rootGNode;
    if (pParent)
    {
        pRootGNodeForCollector = pParent->GetGraphicsNode();
    }
    OwnerPtr<IGraphicsNodePathCollector> opGNodePathCollector = IGraphicsNodePathCollector::Create(pDoc, pRootGNodeForCollector);
    OwnerPtr<TypeAndIdGNodePathTester> opGNodePathTester = NEW_AS_OWNER_PTR(TypeAndIdGNodePathTester, pGNode->GetType(), pGNode->GetId());
    OwnerPtr<IGraphicsNodePathCollection> opGNodePathCollection = opGNodePathCollector->CollectGraphicsNodes(opGNodePathTester.get());
    const std::vector<OwnerPtr<IGraphicsNodePath>>& opGNodePaths = opGNodePathCollection->GetPaths();

    // 对于IPureGraphicsElement等没有很好设置GNodeId的图元，下面可能为空
    // DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opGNodePaths.size() == 0, L"opGNodePaths为空", L"GDMP", L"2024-03-30");

    const OwnerPtr<IGraphicsNodePath>& opGNodePath = opGNodePaths.at(0);
    const std::vector<WeakPtr<const IGraphicsNode>>& pNodePathPtr = opGNodePath->GetPath();
    std::vector<const IGraphicsNode*> pNodePath;
    std::transform(pNodePathPtr.begin(), pNodePathPtr.end(), std::back_inserter(pNodePath), [](WeakPtr<const IGraphicsNode> pNode) {return pNode.Get(); });
    
    std::vector<gcmp::GraphicsNodeId> gNodeIdPath = CollectGNodeIds(pNodePath);
    if (pParent)
    {
        IGraphicsNodeReference& parentNodeRef = pParent->GetGNodeReference();
        const std::vector<gcmp::GraphicsNodeId>& gParentNodeIdPath = parentNodeRef.GetGraphicsNodeIdPath();
        if (gParentNodeIdPath.size() > 0 && gNodeIdPath.size() > 0 && gNodeIdPath.begin()->AsInt() == gParentNodeIdPath.back().AsInt())
        {
            gNodeIdPath.erase(gNodeIdPath.begin());
        }
        gNodeIdPath.insert(gNodeIdPath.begin(), gParentNodeIdPath.begin(), gParentNodeIdPath.end());
    }
    // 设置给IGraphicsNode_Wrapper，用于创建IGraphiceNodeReference。
    // IGraphiceNodeReference用于在GRep探针节点浏览器中点击节点时，预高亮/高亮/拾取对应的图形节点
    opGNodeWrapper->SetGraphicsNodeIdPath(gNodeIdPath);

    if (IGraphicsGroupBase* pGGroupBase = quick_cast<IGraphicsGroupBase>(pGNode)) // 递归几何图形节点
    {
        int count = pGGroupBase->GetChildrenCount();
        for (int i = 0; i < count; i++)
        {
            IGraphicsNode* pChildGNode = pGGroupBase->GetChildFW(i);
            DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pChildGNode, L"pChildGNode为空",L"GDMP",L"2024-03-30");
            OwnerPtr<IGraphicsNode_Wrapper> opChildGNodeWrapper = CreateGNodeWrapperTree(pDoc, id, pChildGNode, rootGNode, isReadOnly, opGNodeWrapper.get());
            DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opChildGNodeWrapper, L"opChildGNodeWrapper为空",L"GDMP",L"2024-03-30");
            std::vector<gcmp::OwnerPtr<IGraphicsNode_Wrapper>>& children = opGNodeWrapper->GetChildren();
            children.push_back(TransferOwnership(opChildGNodeWrapper));
        }
    }
    else if (IGraphicsBRepBody* pGBrepBody = quick_cast<IGraphicsBRepBody>(pGNode)) // 展开BRep图形节点
    {
        IGraphicsNodeList* pEdgeList = pGBrepBody->GetEdgeNodesFW();
        for (int i = 0; i < pEdgeList->GetCount(); i++)
        {
            IGraphicsNode* pChildGNode = pEdgeList->GetItemByIndexFW(i);
            OwnerPtr<IGraphicsNode_Wrapper> opChildGNodeWrapper = CreateGNodeWrapperTree(pDoc, id, pChildGNode, rootGNode, isReadOnly, opGNodeWrapper.get());
            DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opChildGNodeWrapper, L"opChildGNodeWrapper为空",L"GDMP",L"2024-03-30");
            std::vector<gcmp::OwnerPtr<IGraphicsNode_Wrapper>>& children = opGNodeWrapper->GetChildren();
            children.push_back(TransferOwnership(opChildGNodeWrapper));
        }

        IGraphicsNodeList* pFaceList = pGBrepBody->GetFaceNodesFW();
        for (int i = 0; i < pFaceList->GetCount(); i++)
        {
            IGraphicsNode* pChildGNode = pFaceList->GetItemByIndexFW(i);
            OwnerPtr<IGraphicsNode_Wrapper> opChildGNodeWrapper = CreateGNodeWrapperTree(pDoc, id, pChildGNode, rootGNode, isReadOnly, opGNodeWrapper.get());
            DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opChildGNodeWrapper, L"opChildGNodeWrapper为空",L"GDMP",L"2024-03-30");
            std::vector<gcmp::OwnerPtr<IGraphicsNode_Wrapper>>& children = opGNodeWrapper->GetChildren();
            children.push_back(TransferOwnership(opChildGNodeWrapper));
        }
    }
    else if (IGraphicsElementShapeReference* pGRepReference = quick_cast<IGraphicsElementShapeReference>(pGNode)) // 几何引用图形节点
    {
        ElementId refId = pGRepReference->GetReferencedElementId();
        IElement* pElem = pDoc->GetElement(refId);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElem, L"pElem为空",L"GDMP",L"2024-03-30");
        IElementModelShape* pElementModelShape = pElem->GetElementModelShape();
        if (pElementModelShape)
        {
            const IGraphicsElementShape* pSourceGRep = pElementModelShape->GetGraphicsElementShape();
            if (pSourceGRep)
            {
                OwnerPtr<IGraphicsElementShape> opGRep = TransferOwnershipCast<IGraphicsElementShape>(pSourceGRep->Clone());
                opGRep->Transform(pGRepReference->GetTransformMatrix()); // 坐标变换

                isReadOnly = true;
                OwnerPtr<IGraphicsNode_Wrapper> opChildGNodeWrapper = CreateGNodeWrapperTree(pDoc, id, opGRep.get(), rootGNode, isReadOnly, opGNodeWrapper.get());
                DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opChildGNodeWrapper, L"opChildGNodeWrapper为空",L"GDMP",L"2024-03-30");
                opChildGNodeWrapper->CacheGRep(TransferOwnership(opGRep));
                std::vector<gcmp::OwnerPtr<IGraphicsNode_Wrapper>>& children = opGNodeWrapper->GetChildren();
                children.push_back(TransferOwnership(opChildGNodeWrapper));
            }
        }
    }
    return TransferOwnership(opGNodeWrapper);
}

